// 头文件导入
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
// #include <winsock.h>
// #include <winsock2.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define MAXLINE 1024 // 缓冲区大小
#define TRUE 1

int main(int argc, char **argv)
{
    int sockfd, fd, n, m; // 这里的sockfd是网络连接的套接字文件描述符，用于标记套接字接口
    char line[MAXLINE + 1]; // 定义用于传输数据的缓冲区
    struct sockaddr_in6 servaddr, cliaddr; // 定义服务器地址和客户端地址
    time_t t0 = time(NULL); // 定义时间变量，表示从Unix时间戳开始的时间
    printf("time #: %ld\n", t0); // 打印上述时间变量
    fputs(ctime(&t0), stdout); // 使用ctime函数格式化打印出当前的本地时间
    // 创建套接字，并判断是否创建失败
    if ((sockfd = socket(AF_INET6, SOCK_STREAM, 0)) < 0)
        // 当套接字文件描述符为负数的时候，创建失败
        perror("socket error"); // perror用于输出系统错误

    bzero(&servaddr, sizeof(servaddr)); // 将服务器地址所在的内存均写为0
    servaddr.sin6_family = AF_INET6; // 设置套接字地址类型为AF_INET6，即只接受IPv6地址
    servaddr.sin6_port = htons(20000); // 监听20000端口，其中htons把电脑上数据存储顺序的无
    servaddr.sin6_addr = in6addr_any; // 服务器IPv6地址，这里in6addr_any表示将套接字绑定在所有网络接口上
    // 将套接字绑定到指定地址上，并判断是否绑定失败
    if (bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1)
        perror("bind error");
    if (listen(sockfd, 5) == -1)
        perror("listen error");
    while (TRUE)
    {
        printf("> Waiting clients ...\r\n");
        socklen_t clilen = sizeof(struct sockaddr); // 定义socklen_t类变量clilen用来记录
        fd = accept(sockfd, (struct sockaddr *)&cliaddr, &clilen); // 使套接字开始接收客户
        if (fd == -1)
        {
            // 当文件描述符为-1时，请求接收失败
            perror("accept error");
        } printf("> Accepted.\r\n");
        // 从接收到的文件描述符中把数据读取到缓冲区line中，n为读取到的数据的长度
        while ((n = read(fd, line, MAXLINE)) > 0)
        {
            line[n] = 0; // 将读取到的数据的下一位写0，便于输出
            if (fputs(line, stdout) == EOF)
                // 输出失败，引起错误
                perror("fputs error");
        } // 关闭文件描述符
        close(fd);
    }
}